package com.piece.core.framework.util.net;

import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.Map;

/**
 * 获取IP方法
 */
@Slf4j
public class IpUtil {

    /**
     * 获取ip地址
     */
    public static String getIp(HttpServletRequest request) {
        return getIp(request, false);
    }

    /**
     * 获取ip地址
     *
     * @param request
     * @param internalAnalytical 是否解析内网ip为外网ip
     */
    public static String getIp(HttpServletRequest request, boolean internalAnalytical) {
        if (null == request) {
            return "unknown";
        }
        String ip = request.getHeader("x-forwarded-for");
        if (null == ip || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (null == ip || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (null == ip || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (null == ip || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (null == ip || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (null == ip || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (null == ip || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Remote Address");
        }

        if (null == ip || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        if (null == ip || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = "127.0.0.1";
        }

        ip = "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;

        int index = ip.indexOf(",");
        if (index != -1) {
            ip = ip.substring(0, index);
        }

        if ("127.0.0.1".equals(ip) || "localhost".equals(ip) || (internalIp(ip) && internalAnalytical)) {
            // 内网ip找到对应外网ip
            ip = getV4IP(ip);
        }

        return ip;
    }

    /**
     * 是否是内网IP
     *
     * @param ip
     * @return
     */
    public static boolean internalIp(String ip) {
        byte[] addr = textToNumericFormatV4(ip);
        if (addr == null) {
            return false;
        }
        return internalIp(addr) || "127.0.0.1".equals(ip);
    }

    /**
     * 获取外网ip
     */
    public static String getV4IP(String defaultIp) {
        try {
            //String chinaz = "http://ip.chinaz.com";
            String amazon = "http://checkip.amazonaws.com/";
            String result = grepHtmlByUrl(amazon, "utf-8", 5000);
            if (result != null) {
                result = result.replaceAll("(\\\r\\\n|\\\r|\\\n|\\\n\\\r)", "");
                return result;
            }
        } catch (Exception e) {
            return defaultIp;
        }

        return defaultIp;
    }

    /**
     * 根据URL和编码获取网页内容
     *
     * @param url      指定url地址
     * @param encoding 编码格式
     * @param timeout  连接超时时间
     * @return
     */
    public static String grepHtmlByUrl(String url, String encoding, Integer timeout) {
        StringBuffer buffer = new StringBuffer();
        URL urlObj = null;
        URLConnection urlConnection = null;
        InputStreamReader inputStreamReader = null;
        BufferedReader reader = null;
        try {
            // 建立网络连接
            urlObj = new URL(url);
            // 打开网络连接
            urlConnection = urlObj.openConnection();
            if (timeout != null) {
                urlConnection.setConnectTimeout(timeout);
            }
            urlConnection.addRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
            // 创建输入流
            inputStreamReader = new InputStreamReader(urlConnection.getInputStream(), encoding);
            // 创建一个缓冲写入流
            reader = new BufferedReader(inputStreamReader);
            String line;
            while (null != (line = reader.readLine())) {
                // 一行一行追加
                buffer.append(line + "\r\n");
            }
            return buffer.toString();
        } catch (Exception e) {
            return null;
        } finally {
            try {
                if (null != inputStreamReader) {
                    inputStreamReader.close();
                }
            } catch (IOException e) {
                log.error("关闭Http连接异常：{}", e.getMessage());
            }
        }
    }

    private static boolean internalIp(byte[] addr) {
        final byte b0 = addr[0];
        final byte b1 = addr[1];
        // 10.x.x.x/8
        final byte SECTION_1 = 0x0A;
        // 172.16.x.x/12
        final byte SECTION_2 = (byte) 0xAC;
        final byte SECTION_3 = (byte) 0x10;
        final byte SECTION_4 = (byte) 0x1F;
        // 192.168.x.x/16
        final byte SECTION_5 = (byte) 0xC0;
        final byte SECTION_6 = (byte) 0xA8;
        switch (b0) {
            case SECTION_1:
                return true;
            case SECTION_2:
                if (b1 >= SECTION_3 && b1 <= SECTION_4) {
                    return true;
                }
            case SECTION_5:
                switch (b1) {
                    case SECTION_6:
                        return true;
                }
            default:
                return false;
        }
    }

    /**
     * 将IPv4地址转换成字节
     *
     * @param ip IPv4地址
     * @return byte 字节
     */
    public static byte[] textToNumericFormatV4(String ip) {
        if (ip.length() == 0) {
            return null;
        }
        byte[] bytes = new byte[4];
        String[] elements = ip.split("\\.", -1);
        if (ip.indexOf(":") > 0) {
            elements = ip.substring(0, ip.indexOf(":")).split("\\.", -1);
        }
        try {
            long l;
            int i;
            switch (elements.length) {
                case 1:
                    l = Long.parseLong(elements[0]);
                    if ((l < 0L) || (l > 4294967295L))
                        return null;
                    bytes[0] = (byte) (int) (l >> 24 & 0xFF);
                    bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 2:
                    l = Integer.parseInt(elements[0]);
                    if ((l < 0L) || (l > 255L))
                        return null;
                    bytes[0] = (byte) (int) (l & 0xFF);
                    l = Integer.parseInt(elements[1]);
                    if ((l < 0L) || (l > 16777215L))
                        return null;
                    bytes[1] = (byte) (int) (l >> 16 & 0xFF);
                    bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 3:
                    bytes = calculateV4(bytes, elements, 2);
                    l = Integer.parseInt(elements[2]);
                    if ((l < 0L) || (l > 65535L))
                        return null;
                    bytes[2] = (byte) (int) (l >> 8 & 0xFF);
                    bytes[3] = (byte) (int) (l & 0xFF);
                    break;
                case 4:
                    bytes = calculateV4(bytes, elements, 4);
                    break;
                default:
                    return null;
            }
        } catch (NumberFormatException e) {
            log.error("转换IP地址异常：{}", e.getMessage());
            return null;
        }
        return bytes;
    }

    private static byte[] calculateV4(byte[] bytes, String[] elements, int number) {
        long l;
        for (int i = 0; i < number; ++i) {
            l = Integer.parseInt(elements[i]);
            if ((l < 0L) || (l > 255L))
                return null;
            bytes[i] = (byte) (int) (l & 0xFF);
        }
        return bytes;
    }

    /**
     * 获取host ip
     */
    public static String getHostIp() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            log.error("获取host ip异常：{}", e.getMessage());
        }
        return "127.0.0.1";
    }

    /**
     * 获取host name
     */
    public static String getHostName() {
        try {
            return InetAddress.getLocalHost().getHostName();
        } catch (UnknownHostException e) {
            log.error("获取host name异常：{}", e.getMessage());
        }
        return "未知";
    }

    /**
     * 获取网卡序列号
     */
    public static final String getNetCardNo() {
        String address = "";
        String command = "cmd.exe /c ipconfig /all";
        try {
            Process p = Runtime.getRuntime().exec(command);
            BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String line;
            while (null != (line = br.readLine())) {
                if (line.indexOf("DUID") > 0) {
                    int index = line.indexOf(":");
                    index += 2;
                    address = line.substring(index);
                    break;
                }
            }
            br.close();
        } catch (IOException e) {
        }
        return address;
    }

    /**
     * IP地址编码
     */
    public static final int encodeIpToInt(String ip) {
        return (int) encodeIpToLong(ip);
    }

    /**
     * IP地址编码
     */
    public static final long encodeIpToLong(String ip) {
        long result = 0, value;
        final String[] array = ip.split("\\.");
        for (int index = 3; index >= 0; index--) {
            value = Long.parseLong(array[3 - index]);
            result |= value << (index * 8);
        }
        return result;
    }

    /**
     * IP地址解码
     */
    public static final String decodeIntToIp(int value) {
        return decodeLongToIp(Integer.toUnsignedLong(value));
    }

    /**
     * IP地址解码
     */
    public static final String decodeLongToIp(long value) {
        return
                ((value >> 24) & 0xFF) + "." +
                        ((value >> 16) & 0xFF) + "." +
                        ((value >> 8) & 0xFF) + "." +
                        (value & 0xFF);
    }

    /**
     * 端口编码
     */
    public static final short encodePort(int port) {
        return (short) port;
    }

    /**
     * 端口解码
     */
    public static final int decodePort(short port) {
        return Short.toUnsignedInt(port);
    }
}
